#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include "main.h"
#define	BUFFER_CNT 4
#define  FRAME_FORMAT V4L2_PIX_FMT_RGB565

unsigned char RGB24buf;

struct video_buffer
{
    unsigned char *start;
    unsigned int length;
};
int init_device(int fd,struct video_buffer buf[],int *pw,int *ph);
void  start_device(int fd);
void stop_device(int fd);
void process_image(unsigned char *start,int length,int width,int heigh);
int process_image_net(unsigned char *start,int length,int width,int heigh,int sockfd);
int init_socket();
int main(int argc, char *argv[])
{
    int fd;//保存打开视频设备的文件描述符
    int ret;//保存函数返回结果
    int width,heigh;//保存采集视频的宽和高
    int sockfd,clientfd;//sockfd:保存侦听套接字描述府；clientfd:保存accept成功后的 通信套接字描述府
    struct sockaddr_in client_addr;//保存连接方的地址信息；
    struct video_buffer user_buffer[BUFFER_CNT];//用来保存映射后的缓冲区信息
    struct v4l2_buffer v4l2buffer;//一些v4l2的ioctl需要这样一个参数
    socklen_t sock_size;//保存地址结构长度
    sock_size=sizeof(struct sockaddr);
    if(argc<4)
    {
        printf("usage  examples: ./cameraclient /dev/video0  320 240 ");
        return 0;
    }
    fd=open(argv[1],O_RDWR);//打开视频设备
    if(fd<0)
    {
        perror("open video failed:");
        return 0;
    }
    sscanf(argv[2],"%d",&width);//解析宽
    sscanf(argv[3],"%d",&heigh);//解析高
    ret=init_device(fd,user_buffer,&width,&heigh);//初始化设备
    if(ret<0)
    {
        close(fd);
        return 0;
    }
    start_device(fd);//启动视频采集
    v4l2buffer.memory=V4L2_MEMORY_MMAP;
    v4l2buffer.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
#if 0
    while(1)
    {
        ret=ioctl(fd,VIDIOC_DQBUF,&v4l2buffer);
        if(ret<0)
        {
            close(fd);
            return 0;
        }
        process_image(user_buffer[v4l2buffer.index].start,
                      v4l2buffer.bytesused,
                      width,
                      heigh,clientfd);
        ret=ioctl(fd,VIDIOC_QBUF,&v4l2buffer);
        if(ret<0)
        {
            close(fd);
            return 0;
        }
    }
#endif
#if 1
    sockfd=init_socket();//初始化一个侦听套接字
    if(sockfd<0)
    {
        close(fd);
        return 0;
    }
    while((clientfd=accept(sockfd,(struct sockaddr*)&client_addr,&sock_size))>0)
    {
        printf("A connection come!\n");
        while(1)
        {
            ret=ioctl(fd,VIDIOC_DQBUF,&v4l2buffer);//获取一帧数据
            if(ret<0)
            {
                close(fd);
                return 0;
            }
/*!!!!*/
			ConvertYUYVtoRGB565(user_buffer[v4l2buffer.index].start, &RGB24buf, 320, 240);			

            ret=process_image_net(&RGB24buf,
                                  v4l2buffer.bytesused,
                                  width,
                                  heigh,clientfd);
            if(ret<0)
            {
                perror("send error:");
                close(clientfd);
                break;
            }
            ret=ioctl(fd,VIDIOC_QBUF,&v4l2buffer);//将处理后的帧和重新放回请求队列
            if(ret<0)
            {
                close(fd);
                return 0;
            }
        }
    }
#endif
    stop_device(fd);
    close(fd);
}

int init_device(int fd,struct video_buffer buf[],int *pw,int *ph)
{
    int ret;
    int i;
    unsigned char *p;
    struct v4l2_format format;//VIDIOC_S_FMT的参数
    struct v4l2_requestbuffers reqbufs;//VIDIOC_REQBUFS的参数
    struct v4l2_buffer v4l2buf;//VIDIOC_QUERYBUF
	//手动设置摄像头格式
    format.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    format.fmt.pix.height=*ph;
    format.fmt.pix.width=*pw;
    format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//V4L2_PIX_FMT_RGB565;
    format.fmt.pix.field=V4L2_FIELD_INTERLACED;
    ret=ioctl(fd,VIDIOC_S_FMT,&format);//设置采集格式
    if(ret<0)
    {
		perror("VIDIOC_S_FMT");
        return ret;
    }
    ret=ioctl(fd,VIDIOC_G_FMT,&format);//获取采集格式，因为内核可能会修改请求采集的的宽和高
    if(ret<0)
    {
		perror("VIDIOC_G_FMT");
        return ret;
    }
    *ph=format.fmt.pix.height;
    *pw=format.fmt.pix.width;

    reqbufs.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    reqbufs.count=BUFFER_CNT;
    reqbufs.memory=V4L2_MEMORY_MMAP;
    ret=ioctl(fd,VIDIOC_REQBUFS,&reqbufs);//申请视频缓冲区，驱动有可能会修改所求的缓冲区的个数
    if(ret<0)
    {
        return ret;
    }
    for(i=0;i<reqbufs.count;i++)
    {

        v4l2buf.index=i;
        v4l2buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
        v4l2buf.memory=V4L2_MEMORY_MMAP;
        ret=ioctl(fd,VIDIOC_QUERYBUF,&v4l2buf);//查询视频缓冲区
        if(ret<0)
        {
            return ret;
        }
        p=(unsigned char*)mmap(0,v4l2buf.length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,v4l2buf.m.offset);//映射缓冲区
        if(p==MAP_FAILED)
            return -1;
		//保存映射后的缓冲区的信息：起始地址、长度
        buf[i].start=p;
        buf[i].length=v4l2buf.length;
        ret=ioctl(fd,VIDIOC_QBUF,&v4l2buf);//将缓冲区加入到视频请求队列里面
        if(ret<0)
        {
            return ret;
        }
    }
}

void start_device(int fd)
{
    enum v4l2_buf_type type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ioctl(fd,VIDIOC_STREAMON,&type);
}

void stop_device(int fd)
{
    enum v4l2_buf_type type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ioctl(fd,VIDIOC_STREAMOFF,&type);
}

void process_image(unsigned char *start,int length,int width,int heigh)
{
    int fd=open("/dev/fb0",O_RDWR);
    int i,j;
    unsigned short *p1=(unsigned short*)start;
    unsigned short *fbmem=
            (unsigned short*)mmap (NULL ,
                                   320*240*2,
                                   PROT_READ | PROT_WRITE,
                                   MAP_SHARED,
                                   fd, 0);

    for(j=0;j<heigh && j<240;j++)
    {
        for(i=0;i<width && i<320;i++)
        {
            *(fbmem+j*320+i)=*(p1+j*width+i);

        }
    }
    munmap(fbmem,320*240*2);
    close(fd);
    fputc ('.', stdout);
    fflush (stdout);
}

int process_image_net(unsigned char *start,int length,int width,int heigh,int clientfd)
{
    int ret;
    while(1)
    {
        if(length>1024)
        {
            ret=send(clientfd,start,1024,0);
            if(ret<0)
                return ret;
            length-=1024;
            start+=1024;
        }
        else
        {
            ret=send(clientfd,start,length,0);
            return ret;
        }
    }
}

int init_socket()
{
    struct sockaddr_in server_addr;
    int sockfd;
    int ret;
    int i=1;
    socklen_t sock_size;
    sock_size=sizeof(struct sockaddr);
    server_addr.sin_family=AF_INET;
    server_addr.sin_port=htons(12345);
    server_addr.sin_addr.s_addr=INADDR_ANY;
    memset(&(server_addr.sin_zero),0,8);
    sockfd=socket(AF_INET,SOCK_STREAM,0);
    setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(i));
    ret=bind(sockfd,(struct sockaddr*)&server_addr,sock_size);
    if(ret<0)
    {
        perror("Bind error:");
        return -1;
    }
    printf("Bind success!\n");
    ret=listen(sockfd,5);
    if(ret<0)
    {
        perror("Listen error:");
        return -1;
    }
    return sockfd;
}


